using System;

namespace Nivot.PowerShell.WindowsMobile.Rapi2
{
    /// <summary>
    /// Manages connection status and availability of remote devices.
    /// </summary>
    /// <example>
    /// RemoteDeviceManager r = new RemoteDeviceManager();
    ///	r.DeviceDisconnected += new EventHandler(r_DeviceDisconnected);
    ///	RemoteDevice dev = r.Devices.FirstConnectedDevice;
    ///	if (dev == null)
    ///		return;
    ///
    ///	Console.WriteLine(dev.Name + ":" + dev.Platform);
    /// </example>
    public class RemoteDeviceManager : IDisposable
    {
        private static IRAPIDesktop iDesktop;
        private RAPISink iSink;

        static RemoteDeviceManager()
        {
            RAPI2 r2 = new RAPI2();
            iDesktop = (IRAPIDesktop) r2;
        }

        /// <summary>
        /// Creates a new instance of <c>RemoteDeviceManager</c>.
        /// </summary>
        public RemoteDeviceManager()
        {
            iSink = new RAPISink();
            iSink.DeviceConnected += new EventHandler(OnDeviceConnected);
            iSink.DeviceDisconnected += new EventHandler(OnDeviceDisconnected);
            this.Devices = new RAPIDeviceList(iDesktop);
        }

        /// <summary>
        /// Gets a list of connected <see cref="RemoteDevice"/>.
        /// </summary>
        public RAPIDeviceList Devices { get; private set; }

        #region IDisposable Members

        /// <summary>
        /// Cleans up all internal references.
        /// </summary>
        public void Dispose()
        {
            iSink.DeviceConnected -= OnDeviceConnected;
            iSink.DeviceDisconnected -= OnDeviceDisconnected;
            iSink = null;
            this.Devices = null;
            GC.SuppressFinalize(this);
        }

        #endregion

        /// <summary>Occurs when a device connects.</summary>
        public event EventHandler DeviceConnected;

        /// <summary>Raises the <see cref="E:RemoteDeviceManager.DeviceConnected"/> event.</summary>
        protected void OnDeviceConnected(object sender, EventArgs e)
        {
            EventHandler temp = DeviceConnected;
            if (temp != null)
            {
                temp(null, EventArgs.Empty);
            }
        }

        /// <summary>Occurs when a device disconnects.</summary>
        public event EventHandler DeviceDisconnected;

        /// <summary>Raises the <see cref="E:RemoteDeviceManager.DeviceDisconnected"/> event.</summary>
        protected void OnDeviceDisconnected(object sender, EventArgs e)
        {
            EventHandler temp = DeviceDisconnected;
            if (temp != null)
            {
                temp(null, EventArgs.Empty);
            }
        }

        #region Nested type: RAPIDeviceList

        /// <summary>
        /// Enumerates all connected devices. Access through <see cref="RemoteDeviceManager.Devices"/>.
        /// </summary>
        public class RAPIDeviceList : System.Collections.Generic.IEnumerable<RemoteDevice>, IDisposable
        {
            private IRAPIDesktop iDesktop;

            internal RAPIDeviceList(IRAPIDesktop desktop)
            {
                iDesktop = desktop;
            }

            /// <summary>
            /// Gets the first connected device. Returns null if no devices are connected.
            /// </summary>
            public RemoteDevice FirstConnectedDevice
            {
                get
                {
                    foreach (RemoteDevice dev in this)
                        return dev;
                    return null;
                }
            }

            #region IDisposable Members

            /// <summary>
            /// Cleans up all internal references.
            /// </summary>
            public void Dispose()
            {
                iDesktop = null;
                GC.SuppressFinalize(this);
            }

            #endregion

            #region IEnumerable<RemoteDevice> Members

            /// <summary>
            /// Returns the strongly typed enumerator.
            /// </summary>
            /// <returns>Enumerator</returns>
            public System.Collections.Generic.IEnumerator<RemoteDevice> GetEnumerator()
            {
                return new RAPIDeviceEnum(iDesktop);
            }

            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
            {
                return this.GetEnumerator();
            }

            #endregion

            #region Nested type: RAPIDeviceEnum

            /// <summary>
            /// Internal enumerator for connected devices.
            /// </summary>
            public class RAPIDeviceEnum : System.Collections.Generic.IEnumerator<RemoteDevice>
            {
                private RemoteDevice current = null;
                private IRAPIEnumDevices iEnum;

                internal RAPIDeviceEnum(IRAPIDesktop desktop)
                {
                    iEnum = desktop.EnumDevices();
                }

                #region IEnumerator<RemoteDevice> Members

                /// <summary>
                /// Gets the current item in the enumeration.
                /// </summary>
                public RemoteDevice Current
                {
                    get
                    {
                        if (current == null)
                        {
                            throw new InvalidOperationException();
                        }
                        return current;
                    }
                }

                /// <summary>
                /// Frees all available resources.
                /// </summary>
                public void Dispose()
                {
                    current = null;
                    iEnum = null;
                }

                object System.Collections.IEnumerator.Current
                {
                    get { return this.Current; }
                }

                /// <summary>
                /// Moves to the next device. Under all systems as of 2008, there can only be one connected device.
                /// </summary>
                /// <returns>true if a device was found. Otherwise, false.</returns>
                public bool MoveNext()
                {
                    try
                    {
                        IRAPIDevice iDev = iEnum.Next();
                        if (iDev != null)
                        {
                            current = new RemoteDevice(iDev);
                        }
                        return true;
                    }
                    catch (Exception)
                    {
                        current = null;
                    }
                    return false;
                }

                /// <summary>
                /// Resets the enumeration.
                /// </summary>
                public void Reset()
                {
                    iEnum.Reset();
                    current = null;
                }

                #endregion
            }

            #endregion
        }

        #endregion
    }
}